/**
 * Rubik is a for-testing-purposes simple CMS.
 * Copyright (C) 2010 Antonio Hernandez Diaz <ahdiaz@gmail.com>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 * 
 * $Id: SednaCollection.java 8 2010-04-13 19:10:09Z ahdiaz $
 */

package net.ahdiaz.rubik.model;

import java.io.IOException;
import java.util.Collection;
import java.util.HashMap;

import javax.xml.bind.JAXBException;
import javax.xml.transform.TransformerFactoryConfigurationError;

import net.ahdiaz.rubik.sedna.ConnectionFactory;
import net.ahdiaz.rubik.sedna.SednaMarshaller;
import net.ahdiaz.rubik.sedna.SednaResultReader;
import net.ahdiaz.rubik.sedna.SednaUnmarshaller;
import ru.ispras.sedna.driver.DriverException;
import ru.ispras.sedna.driver.SednaConnection;
import ru.ispras.sedna.driver.SednaSerializedResult;
import ru.ispras.sedna.driver.SednaStatement;

public abstract class SednaCollection implements ISednaCollection {

	protected String colName;
	protected SednaConnection sc;
	
	public SednaCollection() throws DriverException {
		this.colName = null;
		this.sc = ConnectionFactory.getInstance();
	}
	
	public SednaCollection(String colName) throws DriverException {
		this.colName = colName;
		this.sc = ConnectionFactory.getInstance();
	}
	
	public void finalize() {
		ConnectionFactory.close(sc);
	}
	
	protected abstract Class<?> getContext();

	protected abstract String getDocNameFromObject(Object item);
	
	protected Collection<?> query(String query) throws JAXBException, DriverException {

		System.out.println(query);
		sc.begin();
		
		Collection<?> result = null;
		SednaStatement st = sc.createStatement();
		boolean ret = st.execute(query);
		
		if (ret == true) {
			SednaSerializedResult sr = st.getSerializedResult();
			SednaUnmarshaller su = new SednaUnmarshaller(this.getContext());
			SednaResultReader srr = new SednaResultReader(su);
			result = srr.read(sr);
		}
		
		sc.commit();
		return result;
	}
	
	public boolean append(Object item) throws DriverException, JAXBException {
		
		sc.begin();
		
		try {
			
			SednaStatement st = sc.createStatement();
			String s = SednaMarshaller.marshal(item, false, true).toString();
						
			if (colName == null) {
				st.loadDocument(s, this.getDocNameFromObject(item));
			} else {
				st.loadDocument(s, this.getDocNameFromObject(item), colName);
			}
			
		} catch (TransformerFactoryConfigurationError e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		sc.commit();
		return true;
	}
	
	public boolean update(Object item) throws DriverException, JAXBException {
		
		try {
			
			String s = SednaMarshaller.marshal(item, false, true).toString();						
			String query = null;
			
			if (colName != null) {
				query = "UPDATE replace $node in doc('%docName', '%colName')/*[1] with %doc";
			} else {
				query = "UPDATE replace $node in doc('%docName')/*[1] with %doc";
			}
			
			query = query
				.replaceAll("%docName", this.getDocNameFromObject(item))
				.replaceAll("%colName", colName)
				.replaceAll("%doc", s);
			
			this.query(query);
			
		} catch (TransformerFactoryConfigurationError e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		return true;
	}
	
	public boolean remove(Object item) throws DriverException, JAXBException {
				
		String query = null;
		if (colName != null) {
			query = "DROP DOCUMENT '%docName' IN COLLECTION '%colName'";
		} else {
			query = "DROP DOCUMENT '%docName'";
		}
		
		query = query
			.replaceAll("%docName", this.getDocNameFromObject(item))
			.replaceAll("%colName", colName);
		
		this.query(query);
		return true;
	}
	
	public Object readByName(String docName) throws JAXBException, DriverException {

		String query;
		if (colName == null) {
			query = "doc('%docName')";
		} else {
			query = "doc('%docName', '%colName')";
		}
		
		query = query
			.replaceAll("%colName", colName)
			.replaceAll("%docName", docName);
		
		Object result = null;
		Collection<?> c = this.query(query);
		
		if (c.size() == 1) {
			result = c.iterator().next();
		}
		
		return result;
	}

	public Object readByUID(String uid) throws JAXBException, DriverException {

		String query;
		if (colName == null) {
			query = "doc('%docName')";
		} else {
			query = "collection('%colName')/*[@uid='%uid']";
		}
		
		query = query
			.replaceAll("%colName", colName)
			.replaceAll("%uid", uid);
		
		Object result = null;
		Collection<?> c = this.query(query);
		
		if (c.size() == 1) {
			result = c.iterator().next();
		}
		
		return result;
	}

	public Collection<?> readAll() throws JAXBException, DriverException {
		return this.readAll(null);
	}
	
	public Collection<?> readAll(HashMap<String, String> constraints) throws JAXBException, DriverException {
		//query = "doc('%docName', '%colName')//collection[@name='%colName']/document[contains(@name, '%docName')]/@name";

		String query;
		if (colName == null) {
			query = "collection('%colName')/*";
		} else {
			query = "collection('%colName')/*";
		}
		query = query
			.replaceAll("%colName", colName);
		
		Collection<?> c = this.query(query);		
		return c;
	}
	
	public int size() {
		int count = 0;
		try {
			Collection<?> c = this.readAll();
			count = c.size();
		} catch (Exception e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return count;
	}
	
}
